/*
 * CEngine - engine control helper
 * Kannan Ramanathan
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.IO;

namespace PCAD.CAT
{
    public class CEngine
    {
        private bool engineLoaded = false; 
        private string engineName;
        private Process engineProcess;
        private StreamReader engineOutput;
        private StreamWriter engineInput;

        public event DataReadHandler DataRead;

        /// <summary>
        /// Initializes a new instance of the <see cref="CEngine"/> class.
        /// </summary>
        public CEngine() 
        { 
            // Empty constructor 
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CEngine"/> class.
        /// </summary>
        /// <param name="engineName">Name of the engine.</param>
        public CEngine(string engineName)
        {
            this.engineName = engineName;
        }


        /// <summary>
        /// Reads the engine output asynchronously.
        /// </summary>
        /// <returns></returns>
        public void readEngineOutputLineAsynchronous()
        {
            if (isEngineLoaded())
            {
                try
                {
                    engineProcess.BeginOutputReadLine();
                }
                catch (Exception e)
                {
                    handleException(e);                    
                }
            }
        }

        /// <summary>
        /// Handles the OutputDataReceived event of the engineProcess control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Diagnostics.DataReceivedEventArgs"/> instance containing the event data.</param>
        /// <todo>Do we have to do a -= of our handler everytime the user does one? Check on that.</todo>
        private void engineProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            try
            {
                DataRead(((Process)sender).Id,e.Data);
            }
            catch (Exception e1)
            {
                handleException(e1);
            }
        }

#if SYNCH_OK
        /// <summary>
        /// Reads the engine output synchronously.
        /// </summary>
        /// <returns>Engine output (till end of stream)</returns>
        public string readEngineOutputLineSynchronous()
        {
            string retVal = "";
            if (isEngineLoaded())
            {
                retVal = engineOutput.ReadLine();
            }
            return retVal;
        }
#endif

        /// <summary>
        /// Determines whether engine is loaded or not.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if engine is loaded; otherwise, <c>false</c>.
        /// </returns>
        public bool isEngineLoaded()
        {
            return engineLoaded;
        }

        /// <summary>
        /// Sends the engine command.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <returns></returns>
        public bool sendEngineCommand(string command)
        {
            bool retCode = false;
            
            if (String.IsNullOrEmpty(command))
            {
                return true; /// No error occurred. So return true. 
            }
            try
            {
                if (isEngineLoaded())
                {
                    engineInput.Write(command);
                    retCode = true;
                }
            }
            catch (Exception e)
            {
                handleException(e);
                return false; 
            }
            
            return retCode;
        }

        /// <summary>
        /// Sends the engine command after adding a CRLF to the cmd.
        /// </summary>
        /// <param name="command">The command.</param>
        public void sendEngineCommandCRLF(string command)
        {
            command += "\n";
            sendEngineCommand(command);
        }

        

        /// <summary>
        /// Loads the engine.
        /// </summary>
        /// <returns>Status of the load phase - True/False</returns>
        public bool loadEngine()
        {
            bool retCode = false;
           
            try
            {
                if (!string.IsNullOrEmpty(this.engineName))
                {
                    engineProcess = new Process();

                    /// Populate the StartInfo struct. This is used as an input for Process class
                    engineProcess.StartInfo.FileName = this.engineName;
                    engineProcess.StartInfo.CreateNoWindow = true;
                    engineProcess.StartInfo.UseShellExecute = false;
                    engineProcess.StartInfo.RedirectStandardInput = true;
                    engineProcess.StartInfo.RedirectStandardOutput = true;                    

                    /// Setup the asynchronous READ_DATA handler
                    engineProcess.OutputDataReceived +=
                        new DataReceivedEventHandler(engineProcess_OutputDataReceived);

                    /// Execute the engine now
                    retCode = engineProcess.Start();                    

                    if (retCode == true)
                    {
                        /// Store input and output streams
                        engineOutput = engineProcess.StandardOutput;
                        engineInput = engineProcess.StandardInput;                        

                        /// Set a flag for other methods to check [Internal]
                        engineLoaded = true;
                    }
                    else
                    {
                        throw new Exception("Process creation error: Failed to load the engine" 
                            + this.engineName);
                    }
                }
                return retCode;               
            }
            catch(Exception e)
            {
                handleException(e);
                return false;   /// Signal a failure since we have caught an exception
            }
        }

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="e">Exception struct.</param>
        private void handleException(Exception e)
        {
            string s = e.Message + "\n" + e.ToString();
            Console.WriteLine(s);            
        }
    }
}
